import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
var _excluded = ["unRegisterField", "children"];
import React, { useRef, useState, useEffect, useCallback, forwardRef, cloneElement } from 'react';
import Validator from 'async-validator';
import set from 'lodash/set';
import get from 'lodash/get';
import unset from 'lodash/unset';
import isEqual from 'lodash/isEqual';
import isPlainObject from 'lodash/isPlainObject';
import useRealtimeRef from '../useRealtimeRef';
/**
 * 虚拟组件，用于执行销毁函数，为下游元素注入 antd3 所需的校验错误提示
 */
var VirtualField = /*#__PURE__*/forwardRef(function (_ref, ref) {
  var unRegisterField = _ref.unRegisterField,
    children = _ref.children,
    rest = _objectWithoutProperties(_ref, _excluded);
  // 实时缓存最新的销毁函数
  var fnRef = useRealtimeRef(unRegisterField);
  useEffect(function () {
    return function () {
      return fnRef.current();
    };
  }, []);
  return /*#__PURE__*/cloneElement(children, _objectSpread({
    ref: ref
  }, rest));
});
var useForm = function useForm(options) {
  var _ref2 = options || {},
    onValuesChange = _ref2.onValuesChange,
    pure = _ref2.pure;
  /** fieldsMeta 平铺结构 */
  var fieldsMetaRef = useRef({});
  /** fieldsRef 平铺结构，持有字段元素 ref 引用 */
  var fieldsRef = useRef({});
  /** valuesRef 不包含默认值 */
  var valuesRef = useRef({});
  /** 嵌套结构，缓存错误文案 */
  var errorsRef = useRef({});
  /** 重刷页面唯一接口 */
  var _useState = useState({}),
    _useState2 = _slicedToArray(_useState, 2),
    _ = _useState2[0],
    forceUpdate = _useState2[1];
  /**
   * 注册
   * @param name
   * @param meta
   */
  var registerField = useCallback(function (name, meta) {
    setFieldMeta(name, meta || {});
    return function () {
      _unRegisterField(name);
    };
  }, []);
  /**
   * 获取字段名
   * @param names
   */
  var getFieldsName = useCallback(function (names) {
    return names ? names : Object.keys(fieldsMetaRef.current);
  }, []);
  /**
   * 设置元数据
   * @param name
   * @param key
   * @param value
   */
  var setFieldMeta = useCallback(function (name, key, value) {
    if (typeof key !== 'string') {
      fieldsMetaRef.current[name] = key || {};
    } else if (typeof key === 'string') {
      if (!fieldsMetaRef.current[name]) fieldsMetaRef.current[name] = {};
      var meta = fieldsMetaRef.current[name];
      meta[key] = value;
    }
  }, []);
  /**
   * 获取元数据
   * @param name
   * @param key
   */
  var getFieldMeta = useCallback(function (name, key) {
    var meta = fieldsMetaRef.current[name];
    if (meta) {
      if (key) return meta[key];
      return meta;
    }
    // 对嵌套数据的父属性，转换处理，使元数据有值，意为父属性字段存在
    var fieldsMetaData = {};
    getFieldsName().forEach(function (fieldName) {
      set(fieldsMetaData, fieldName, fieldsMetaRef.current[fieldName]);
    });
    var fieldMeta = get(fieldsMetaData, name);
    if (key) return fieldMeta === null || fieldMeta === void 0 ? void 0 : fieldMeta[key];
    return fieldMeta;
  }, [getFieldsName]);
  /**
   * 设置字段引用
   * @param name
   * @param ref
   */
  var setFieldRef = useCallback(function (name, ref) {
    if (ref) {
      fieldsRef.current[name] = ref;
    } else {
      delete fieldsRef.current[name];
    }
  }, []);
  /**
   * 获取字段的 ref
   * @param name
   */
  var getFieldRef = useCallback(function (name) {
    return fieldsRef.current[name];
  }, [fieldsRef]);
  /**
   * 是否有交互行为发生，或手动设置过值
   * @param name
   */
  var isFieldTouched = useCallback(function (name) {
    var currentValue = get(valuesRef.current, name);
    var trasver = function trasver(val) {
      var result;
      // 引用类型，递归判断子属性是否无值
      if (Array.isArray(val)) {
        result = val.every(function (subVal) {
          return trasver(subVal);
        });
      } else if (isPlainObject(val)) {
        result = Object.keys(val).every(function (key) {
          return trasver(val[key]);
        });
      } else {
        /**
         * 与 setFieldsValue 设置值逻辑相同
         * 如果手动设置为 undefined，getFieldsValue 获取到 initialValue，设置空值可使用 null
         */
        result = val !== undefined;
      }
      return result;
    };
    return trasver(currentValue);
  }, []);
  /**
   * 是否有交互行为发生，或手动设置过值
   */
  var isFieldsTouched = useCallback(function () {
    return getFieldsName().some(function (name) {
      return isFieldTouched(name);
    });
  }, [getFieldsName, isFieldTouched]);
  /**
   * 获取初始值
   */
  var getFieldsInitialValue = useCallback(function () {
    var initialValues = {};
    getFieldsName().forEach(function (name) {
      set(initialValues, name, getFieldMeta(name, 'initialValue'));
    });
    return initialValues;
  }, [getFieldsName, getFieldMeta]);
  /**
   * 获取初始值
   * @param name
   */
  var getFieldInitialValue = useCallback(function (name) {
    var initialValues = getFieldsInitialValue();
    return get(initialValues, name);
  }, [getFieldsInitialValue]);
  /**
   * 获取值。设置 initialValue 初始值会引起 getFieldValue 数据变更，但不会引起重绘
   * @param name
   */
  var getFieldValue = useCallback(function (name) {
    var fieldMeta = getFieldMeta(name);
    if (pure && !fieldMeta) return;
    var fieldInitialValue = getFieldInitialValue(name);
    var fieldValue = get(valuesRef.current, name);
    // @FIXME 深层属性判断 touch 与否有问题
    return isFieldTouched(name) ? fieldValue : fieldInitialValue;
  }, [getFieldMeta, getFieldInitialValue, isFieldTouched]);
  /**
   * 获取值
   */
  var getFieldsValue = useCallback(function () {
    var values = {};
    var names = getFieldsName();
    names.forEach(function (name) {
      set(values, name, getFieldValue(name));
    });
    return values;
  }, [getFieldsName, getFieldValue]);
  /**
   * 销毁
   * @param name
   * @param meta
   */
  var _unRegisterField = useCallback(function (name) {
    var fieldsMetaTemp = _objectSpread({}, fieldsMetaRef.current);
    delete fieldsMetaTemp[name];
    fieldsMetaRef.current = fieldsMetaTemp;
    var newValues = _objectSpread({}, valuesRef.current);
    unset(newValues, name);
    valuesRef.current = newValues;
    var newErrors = _objectSpread({}, errorsRef.current);
    unset(newErrors, name);
    errorsRef.current = newErrors;
    forceUpdate({});
    var values = getFieldsValue();
    onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(values);
  }, [getFieldsValue]);
  /**
   * 校验字段，内部
   * @param names
   */
  var _validate = useCallback(function (names, vals, validateOption) {
    var fieldsName = getFieldsName(names); // 平铺结构
    var values = vals ? vals : {};
    var rules = {};
    var innerValidateOption = validateOption ? validateOption : {};
    fieldsName.forEach(function (name) {
      // values 以深层属性为键
      if (!vals) values[name] = getFieldValue(name);
      var fieldRules = getFieldMeta(name, 'rules');
      if (fieldRules) rules[name] = fieldRules;
      if (!validateOption) {
        var validateFirst = getFieldMeta(name, 'validateFirst');
        if (validateFirst) innerValidateOption.first = validateFirst;
      }
    });
    return new Promise(function (resolve, reject) {
      new Validator(rules).validate(values, validateOption, function (errorList) {
        if (!errorList) {
          var errs = _objectSpread({}, errorsRef.current);
          getFieldsName(names).forEach(function (name) {
            set(errs, name, undefined);
          });
          errorsRef.current = errs;
          forceUpdate({});
          // result 转换为嵌套数据
          var result = {};
          Object.keys(values).forEach(function (key) {
            set(result, key, values[key]);
          });
          resolve(result);
        } else {
          var _errs = {};
          errorList.forEach(function (_ref3) {
            var field = _ref3.field,
              message = _ref3.message;
            if (!_errs[field]) _errs[field] = [message];else _errs[field].push(message);
          });
          var newErrors = _objectSpread({}, errorsRef.current);
          getFieldsName(names).forEach(function (name) {
            if (_errs[name]) set(newErrors, name, _errs[name]);else set(newErrors, name, undefined);
          });
          errorsRef.current = newErrors;
          forceUpdate({});
          reject({
            errors: newErrors,
            values: values
          });
        }
      });
    });
  }, [getFieldsName, getFieldValue, getFieldMeta]);
  /**
   * 校验字段
   * @param names
   */
  var validateFields = useCallback(function (names, validateOption) {
    var values = {};
    if (names) {
      getFieldsName(names).forEach(function (name) {
        values[name] = getFieldValue(name);
      });
    }
    return _validate(names, names ? values : undefined, validateOption);
  }, [getFieldsName, getFieldValue, _validate]);
  /**
   * 校验字段
   * @param name
   * @param value
   */
  var validateField = useCallback(function (name, value) {
    // @ts-ignore
    return _validate([name], _defineProperty({}, name, value !== undefined ? value : getFieldValue(name)));
  }, [_validate, getFieldValue]);
  /**
   * 字段组件装饰器
   * @param name
   * @param meta
   */
  var getFieldDecorator = useCallback(function (name) {
    var meta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    registerField(name, meta);
    var _meta$trigger = meta.trigger,
      trigger = _meta$trigger === void 0 ? 'onChange' : _meta$trigger,
      _meta$validateTrigger = meta.validateTrigger,
      validateTrigger = _meta$validateTrigger === void 0 ? 'onChange' : _meta$validateTrigger,
      _meta$valuePropName = meta.valuePropName,
      valuePropName = _meta$valuePropName === void 0 ? 'value' : _meta$valuePropName,
      _meta$getValueFromEve = meta.getValueFromEvent,
      getValueFromEvent = _meta$getValueFromEve === void 0 ? function (e) {
        return e && e.target ? e.target[valuePropName] : e;
      } : _meta$getValueFromEve,
      normalize = meta.normalize;
    return function (inst) {
      var _objectSpread2, _get;
      var instProps = inst.props;
      var value = getFieldValue(name);
      var defaultValue = getFieldInitialValue(name);
      var props = _objectSpread(_objectSpread({}, instProps), {}, (_objectSpread2 = {
        defaultValue: normalize ? normalize(defaultValue) : defaultValue
      }, _defineProperty(_objectSpread2, valuePropName, normalize ? normalize(value) : value), _defineProperty(_objectSpread2, trigger, function () {
        instProps[trigger] && instProps[trigger].apply(instProps, arguments);
        var value = getValueFromEvent ? getValueFromEvent.apply(void 0, arguments) : arguments.length <= 0 ? undefined : arguments[0];
        setFieldValue(name, value);
      }), _objectSpread2));
      var originMethod = props[validateTrigger];
      props[validateTrigger] = function () {
        originMethod && originMethod.apply(void 0, arguments);
        var value = getValueFromEvent ? getValueFromEvent.apply(void 0, arguments) : arguments.length <= 0 ? undefined : arguments[0];
        validateField(name, value);
      };
      var saveRef = function saveRef(ref) {
        if (instProps.ref && typeof instProps.ref == 'function') {
          instProps.ref(ref);
        }
        setFieldRef(name, ref);
      };
      props.ref = saveRef;
      var PropsForAntd3FormItem = {
        'data-name': name,
        'data-field-name': name,
        'data-__field': {
          errors: (_get = get(errorsRef.current, name)) === null || _get === void 0 ? void 0 : _get.map(function (message) {
            return {
              message: message
            };
          }),
          value: value
        },
        'data-__meta': {
          validate: [{
            rules: meta.rules
          }]
        }
      };
      return /*#__PURE__*/React.createElement(VirtualField, _objectSpread({
        unRegisterField: function unRegisterField() {
          _unRegisterField(name);
        }
      }, PropsForAntd3FormItem), /*#__PURE__*/React.cloneElement(inst, props));
    };
  }, [registerField, _unRegisterField, getFieldValue, getFieldInitialValue, validateField, setFieldRef]);
  /**
   * 获取错误文案
   */
  var getFieldsError = useCallback(function () {
    var errs = {};
    var names = getFieldsName();
    names.forEach(function (name) {
      set(errs, name, get(errorsRef.current, name));
    });
    return errs;
  }, [getFieldsName]);
  /**
   * 获取错误文案
   * @param name
   */
  var getFieldError = useCallback(function (name) {
    var fieldMeta = getFieldMeta(name);
    if (pure && !fieldMeta) return;
    return get(errorsRef.current, name);
  }, [getFieldMeta]);
  /**
   * 设置字段的值
   * @param name
   * @param value
   * @param touched
   */
  var setFieldValue = useCallback(function (name, value) {
    if (pure && !getFieldMeta(name)) {
      if (process.env.NODE_ENV === 'development') {
        console.warn("".concat(name, " has not registered."));
      }
      return;
    }
    var values = _objectSpread({}, valuesRef.current);
    set(values, name, value);
    valuesRef.current = values;
    forceUpdate({});
    onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(values);
  }, [getFieldMeta]);
  /**
   * 设置字段的值
   * @param vals
   */
  var setFieldsValue = useCallback(function (vals) {
    var filterVals = {};
    if (pure) {
      Object.keys(vals).forEach(function (name) {
        if (getFieldMeta(name)) {
          filterVals[name] = vals[name];
        } else if (process.env.NODE_ENV === 'development') {
          console.warn("".concat(name, " has not registered."));
        }
      });
    } else {
      filterVals = vals;
    }
    var values = _objectSpread(_objectSpread({}, valuesRef.current), filterVals);
    valuesRef.current = values;
    forceUpdate({});
    onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(values);
  }, [getFieldMeta, getFieldsName]);
  /**
   * 重置为初始值
   */
  var resetFields = useCallback(function (names) {
    if (names) {
      var values = valuesRef.current;
      var errors = errorsRef.current;
      names.forEach(function (name) {
        unset(values, name);
        unset(errors, name);
      });
      valuesRef.current = values;
      errorsRef.current = errors;
    } else {
      valuesRef.current = {};
      errorsRef.current = {};
    }
    forceUpdate({});
  }, [getFieldsName]);
  /**
   * 判断表单是否变更
   */
  var isFormChanged = useCallback(function () {
    var values = getFieldsValue();
    return getFieldsName().some(function (name) {
      var initialValue = getFieldInitialValue(name);
      var value = get(values, name);
      return !isEqual(value, initialValue);
    });
  }, [getFieldsValue, getFieldsName, getFieldInitialValue]);
  /**
   * 判断表单项是否变更
   */
  var isFieldChanged = useCallback(function (name) {
    var initialValue = getFieldInitialValue(name);
    var value = getFieldValue(name);
    return !isEqual(value, initialValue);
  }, [getFieldsValue, getFieldInitialValue]);
  return {
    registerField: registerField,
    unRegisterField: _unRegisterField,
    setFieldMeta: setFieldMeta,
    getFieldMeta: getFieldMeta,
    getFieldDecorator: getFieldDecorator,
    getFieldRef: getFieldRef,
    getFieldsValue: getFieldsValue,
    getFieldValue: getFieldValue,
    getFieldInitialValue: getFieldInitialValue,
    getFieldsError: getFieldsError,
    getFieldError: getFieldError,
    setFieldValue: setFieldValue,
    setFieldsValue: setFieldsValue,
    resetFields: resetFields,
    validateField: validateField,
    validateFields: validateFields,
    isFormChanged: isFormChanged,
    isFieldChanged: isFieldChanged,
    isFieldTouched: isFieldTouched,
    isFieldsTouched: isFieldsTouched
  };
};
export default useForm;